﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Text;
using Kopytko.GameBase;

namespace Kopytko
{
    public class PluginManager : List<GameBase.GameBase>
    {

        #region Singleton
        static PluginManager instance = null;
        static readonly object padlock = new object();

        public PluginManager()
        {

        }

        public static PluginManager Instance
        {
            get
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new PluginManager();
                    }
                    return instance;
                }
            }
        }

        #endregion


        bool isBusy = false;
        public bool IsBusy
        {
            get { return isBusy; }
        }

        public void LoadComponentsAsync(string componentsDirectory)
        {
            LoadComponentsWorkerDelegate worker = new LoadComponentsWorkerDelegate(LoadComponentsWorker);
            AsyncCallback completedCallback = new AsyncCallback(LoadComponentsCompletedCallback);

            lock (sync)
            {
                if (isBusy)
                {
                    throw new InvalidOperationException("Components are currently loading.");
                }

                AsyncOperation async = AsyncOperationManager.CreateOperation(null);
                worker.BeginInvoke(componentsDirectory, async, completedCallback, async);
                isBusy = true;
            }
        }

        public event EventHandler<LoadComponentsProgressChangedEventArgs> LoadComponentsProgressChanged;
        protected virtual void OnLoadComponentsProgressChanged(LoadComponentsProgressChangedEventArgs e)
        {
            if (LoadComponentsProgressChanged != null)
            {
                LoadComponentsProgressChanged(this, e);
            }
        }

        public event AsyncCompletedEventHandler LoadComponentsCompleted;
        protected virtual void OnLoadComponentsCompleted(AsyncCompletedEventArgs e)
        {
            if (LoadComponentsCompleted != null)
            {
                LoadComponentsCompleted(this, e);
            }
        }

        private readonly object sync = new object();

        private delegate void LoadComponentsWorkerDelegate(string componentsDirectory, AsyncOperation async);
        private void LoadComponentsWorker(string componentsDirectory, AsyncOperation async)
        {
            this.Clear();
            string[] componentFiles = Directory.GetFiles(componentsDirectory, "*.dll");

            for (int i = 0; i < componentFiles.Length; i++)
            {
                if (componentFiles[i].Contains("GameBase.dll"))
                    continue;
                
                Assembly componentAssembly = Assembly.LoadFile(componentFiles[i]);
                Type[] componentTypes = componentAssembly.GetTypes();
                foreach (Type componentType in componentTypes)
                {
                    if (typeof(GameBase.GameBase).IsAssignableFrom(componentType))
                    {
                        if (componentType.IsAbstract)
                            continue;

                        this.Add(Activator.CreateInstance(componentType) as GameBase.GameBase);
                    }
                }

                int progressPercentage = 100 * (i + 1) / componentFiles.Length;

                LoadComponentsProgressChangedEventArgs eArgs = new LoadComponentsProgressChangedEventArgs(
                    progressPercentage, new FileInfo(componentFiles[i]).Name, null);
                async.Post(delegate(object e)
                    {
                        OnLoadComponentsProgressChanged(e as LoadComponentsProgressChangedEventArgs);
                    }, eArgs);
            }
        }

        private void LoadComponentsCompletedCallback(IAsyncResult asyncResult)
        {
            LoadComponentsWorkerDelegate worker = (asyncResult as AsyncResult).AsyncDelegate as LoadComponentsWorkerDelegate;
            AsyncOperation async = asyncResult.AsyncState as AsyncOperation;

            worker.EndInvoke(asyncResult);

            lock (sync)
            {
                isBusy = false;
            }

            AsyncCompletedEventArgs completedArgs = new AsyncCompletedEventArgs(null, false, null);

            async.PostOperationCompleted(delegate(object e)
                {
                    OnLoadComponentsCompleted(e as AsyncCompletedEventArgs);
                }, completedArgs);
        }

        public class LoadComponentsProgressChangedEventArgs : ProgressChangedEventArgs
        {
            private string currentComponent;
            public string CurrentComponent
            {
                get { return currentComponent; }
            }

            public LoadComponentsProgressChangedEventArgs(int progressPercentage, string currentComponent,
                object userState)
                : base(progressPercentage, userState)
            {
                this.currentComponent = currentComponent;
            }
        }

    }
}
